const crypto = require('crypto');
const { userCollection } = require('../../common/constants');
const { ERROR } = require('../../common/error');
const { getRedisEnable } = require('./utils');
const { openDataCollection } = require('../../common/constants');

function decryptWeixinData({ encryptedData, sessionKey, iv } = {}) {
    const oauthConfig = this.configUtils.getOauthConfig({
        provider: 'weixin',
    });
    const decipher = crypto.createDecipheriv(
        'aes-128-cbc',
        Buffer.from(sessionKey, 'base64'),
        Buffer.from(iv, 'base64')
    );
    // 设置自动 padding 为 true，删除填充补位
    decipher.setAutoPadding(true);
    let decoded;
    decoded = decipher.update(encryptedData, 'base64', 'utf8');
    decoded += decipher.final('utf8');
    decoded = JSON.parse(decoded);
    if (decoded.watermark.appid !== oauthConfig.appid) {
        throw new Error('Invalid wechat appid in decode content');
    }
    return decoded;
}

function getWeixinPlatform() {
    const platform = this.clientPlatform;
    const userAgent = this.getUniversalClientInfo().userAgent;
    switch (platform) {
        case 'app':
        case 'app-plus':
            return 'app';
        case 'mp-weixin':
            return 'mp';
        case 'h5':
        case 'web':
            return userAgent.indexOf('MicroMessenger') > -1 ? 'h5' : 'web';
        default:
            throw new Error('Unsupported weixin platform');
    }
}

async function saveWeixinUserKey({
    openid,
    sessionKey, // 微信小程序用户sessionKey
    accessToken, // App端微信用户accessToken
    refreshToken, // App端微信用户refreshToken
    accessTokenExpired, // App端微信用户accessToken过期时间
} = {}) {
    // 微信公众平台、开放平台refreshToken有效期均为30天（微信没有在网络请求里面返回30天这个值，务必注意未来可能出现调整，需及时更新此处逻辑）。
    // 此前QQ开放平台有调整过accessToken的过期时间：[access_token有效期由90天缩短至30天](https://wiki.connect.qq.com/%E3%80%90qq%E4%BA%92%E8%81%94%E3%80%91access_token%E6%9C%89%E6%95%88%E6%9C%9F%E8%B0%83%E6%95%B4)

    const appId = this.getUniversalClientInfo().appId;
    const weixinPlatform = getWeixinPlatform.call(this);
    const keyObj = {
        dcloudAppid: appId,
        openid,
        platform: 'weixin-' + weixinPlatform,
    };
    switch (weixinPlatform) {
        case 'mp':
            await this.uniOpenBridge.setSessionKey(
                keyObj,
                {
                    session_key: sessionKey,
                },
                30 * 24 * 60 * 60
            );
            break;
        case 'app':
        case 'h5':
        case 'web':
            await this.uniOpenBridge.setUserAccessToken(
                keyObj,
                {
                    access_token: accessToken,
                    refresh_token: refreshToken,
                    access_token_expired: accessTokenExpired,
                },
                30 * 24 * 60 * 60
            );
            break;
        default:
            break;
    }
}

async function saveSecureNetworkCache({ code, openid, unionid, sessionKey }) {
    const { appId } = this.getUniversalClientInfo();
    const key = `uni-id:${appId}:weixin-mp:code:${code}:secure-network-cache`;
    const value = JSON.stringify({
        openid,
        unionid,
        session_key: sessionKey,
    });
    // 此处存储的是code的缓存，有效期两天即可
    const expiredSeconds = 2 * 24 * 60 * 60;

    await openDataCollection.doc(key).set({
        value,
        expired: Date.now() + expiredSeconds * 1000,
    });
    const isRedisEnable = getRedisEnable();
    if (isRedisEnable) {
        const redis = uniCloud.redis();
        await redis.set(key, value, 'EX', expiredSeconds);
    }
}

function generateWeixinCache({
    sessionKey, // 微信小程序用户sessionKey
    accessToken, // App端微信用户accessToken
    refreshToken, // App端微信用户refreshToken
    accessTokenExpired, // App端微信用户accessToken过期时间
} = {}) {
    const platform = getWeixinPlatform.call(this);
    let cache;
    switch (platform) {
        case 'app':
        case 'h5':
        case 'web':
            cache = {
                access_token: accessToken,
                refresh_token: refreshToken,
                access_token_expired: accessTokenExpired,
            };
            break;
        case 'mp':
            cache = {
                session_key: sessionKey,
            };
            break;
        default:
            throw new Error('Unsupported weixin platform');
    }
    return {
        third_party: {
            [`${platform}_weixin`]: cache,
        },
    };
}

function getWeixinOpenid({ userRecord } = {}) {
    const weixinPlatform = getWeixinPlatform.call(this);
    const appId = this.getUniversalClientInfo().appId;
    const wxOpenidObj = userRecord.wx_openid;
    if (!wxOpenidObj) {
        return;
    }
    return wxOpenidObj[`${weixinPlatform}_${appId}`] || wxOpenidObj[weixinPlatform];
}

async function getWeixinCacheFallback({ userRecord, key } = {}) {
    const platform = getWeixinPlatform.call(this);
    const thirdParty = userRecord && userRecord.third_party;
    if (!thirdParty) {
        return;
    }
    const weixinCache = thirdParty[`${platform}_weixin`];
    return weixinCache && weixinCache[key];
}

async function getWeixinCache({ uid, userRecord, key } = {}) {
    const weixinPlatform = getWeixinPlatform.call(this);
    const appId = this.getUniversalClientInfo().appId;
    if (!userRecord) {
        const getUserRes = await userCollection.doc(uid).get();
        userRecord = getUserRes.data[0];
    }
    if (!userRecord) {
        throw {
            errCode: ERROR.ACCOUNT_NOT_EXISTS,
        };
    }
    const openid = getWeixinOpenid.call(this, {
        userRecord,
    });
    const getCacheMethod = weixinPlatform === 'mp' ? 'getSessionKey' : 'getUserAccessToken';
    const userKey = await this.uniOpenBridge[getCacheMethod]({
        dcloudAppid: appId,
        platform: 'weixin-' + weixinPlatform,
        openid,
    });
    if (userKey) {
        return userKey[key];
    }
    return getWeixinCacheFallback({
        userRecord,
        key,
    });
}

async function getWeixinAccessToken() {
    const weixinPlatform = getWeixinPlatform.call(this);
    const appId = this.getUniversalClientInfo().appId;

    const cache = await this.uniOpenBridge.getAccessToken({
        dcloudAppid: appId,
        platform: 'weixin-' + weixinPlatform,
    });

    return cache.access_token;
}
module.exports = {
    decryptWeixinData,
    getWeixinPlatform,
    generateWeixinCache,
    getWeixinCache,
    saveWeixinUserKey,
    getWeixinAccessToken,
    saveSecureNetworkCache,
};
